'use client';

import './style.scss';

import { use, useCallback, useEffect, useState } from 'react';
import Link from 'next/link';
import { useRouter, useSearchParams } from 'next/navigation';
import { CreditCard, ExternalLink, Gamepad2, Package, ShoppingCart, Ticket } from 'lucide-react';
import { fetchApi } from '@/lib/utils/client';
import Loading from '@/app/component/Loading';
import PayButton, { type PayButtonState } from '@/app/component/PayButton';
import useAuth from '@/hooks/useAuth';
import useCart from '@/hooks/useCart';
import type {
	ProductDetail,
	ProductType,
	ChannelSearchResponse,
	ChannelSearchRow
} from '@/types/store';

/** 최근 후원 채널 localStorage MRU. 최신 사용 순(newest first), 최대 5개. */
const RECENT_KEY = 'antooza:recent-donation-channels';
const RECENT_MAX = 5;

function loadRecent(): ChannelSearchRow[]
{
	if (typeof window === 'undefined') {
		return [];
	}
	try {
		const raw = localStorage.getItem(RECENT_KEY);
		return raw ? JSON.parse(raw) as ChannelSearchRow[] : [];
	}
	catch {
		return [];
	}
}

function saveRecent(list: ChannelSearchRow[])
{
	if (typeof window === 'undefined') {
		return;
	}
	localStorage.setItem(RECENT_KEY, JSON.stringify(list.slice(0, RECENT_MAX)));
}

function addRecent(ch: ChannelSearchRow)
{
	const cur = loadRecent().filter(c => c.id !== ch.id);
	saveRecent([ch, ...cur]);
}

function removeRecent(id: number)
{
	saveRecent(loadRecent().filter(c => c.id !== id));
}

const TYPE_BADGE_CLASS: Record<ProductType, string> = {
	1: 'bg-sky-100 text-sky-800 dark:bg-sky-900/40 dark:text-sky-300',
	2: 'bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-300'
};

const TYPE_LABEL: Record<ProductType, string> = { 1: '실물', 2: '쿠폰' };

function TypeBadge({ type, size = 'sm' }: { type: ProductType; size?: 'sm'|'md' })
{
	const Icon = type === 2 ? Ticket : Package;
	const padCls = size === 'md' ? 'p-1.5' : 'p-1';
	const iconCls = size === 'md' ? 'size-3.5' : 'size-3';

	return (
		<span
			title={TYPE_LABEL[type]}
			aria-label={TYPE_LABEL[type]}
			className={`inline-flex items-center justify-center rounded ${padCls} ${TYPE_BADGE_CLASS[type]}`}
		>
			<Icon className={iconCls} />
		</span>
	);
}

const HARD_MAX = 999;

function formatPrice(n: number): string
{
	return n.toLocaleString('ko-KR');
}

function clampQuantity(value: number, stock: number, minPurchase: number, maxPurchase: number): number
{
	const stockCap = stock > 0 ? Math.min(stock, HARD_MAX) : HARD_MAX;
	const upper = Math.min(maxPurchase, stockCap);
	return Math.max(minPurchase, Math.min(value, upper));
}

export default function StoreDetailPage({ params }: { params: Promise<{ id: string }> })
{
	const { id } = use(params);
	const productID = parseInt(id, 10);
	const router = useRouter();
	const searchParams = useSearchParams();
	const codeParam = searchParams.get('code');
	const { loginCheck } = useAuth();
	const { addItem, setChannel } = useCart();

	const [product, setProduct] = useState<ProductDetail|null>(null);
	const [loading, setLoading] = useState(true);
	const [error, setError] = useState<string|null>(null);

	const [quantity, setQuantity] = useState(1);

	// 상품 로드 후 초기 수량을 minPurchase로 세팅
	useEffect(() => {
		if (product) {
			setQuantity(product.minPurchase);
			setOptedOut(false); // 상품 전환 시 이전 상품의 선택안함 상태 잔존 방지
		}
	}, [product]);

	const [selectedChannel, setSelectedChannel] = useState<ChannelSearchRow|null>(null);
	const [optedOut, setOptedOut] = useState(false);
	const [channelModalOpen, setChannelModalOpen] = useState(false);
	const [pendingPurchase, setPendingPurchase] = useState(false);

	const [payState] = useState<PayButtonState>('idle');
	const [submitError, setSubmitError] = useState<string|null>(null);
	const [tab, setTab] = useState<'product'|'game'>('product');

	const load = useCallback(async () => {
		setLoading(true);
		const res = await fetchApi<ProductDetail>(`/api/store/products/${productID}`, { silent: true });

		if (res.success && res.data) {
			setProduct(res.data);
			setError(null);
		}
		else {
			setError(res.message || '상품을 불러올 수 없습니다.');
		}

		setLoading(false);
	}, [productID]);

	useEffect(() => {
		load();
	}, [load]);

	// querystring ?code=XXX → 후원 채널 자동 설정 (case-insensitive 검증, 무효 시 조용히 무시)
	useEffect(() => {
		if (!codeParam) {
			return;
		}

		let cancelled = false;

		(async () => {
			const res = await fetchApi<ChannelSearchRow>(`/api/store/channels/by-code?code=${encodeURIComponent(codeParam)}`, { silent: true });

			if (cancelled) {
				return;
			}

			if (res.success && res.data) {
				setSelectedChannel(res.data);
				addRecent(res.data);
			}
		})();

		return () => { cancelled = true; };
	}, [codeParam]);

	const proceedPurchase = (channel: ChannelSearchRow|null) => {
		if (!product) {
			return;
		}

		// 바로 구매: 현재 선택 상품을 cart 에 add 후 /checkout 으로 이동.
		// 기존 cart 항목은 보존되며 함께 결제됨.
		addItem({
			productID: product.id,
			name: product.name,
			thumbnail: product.thumbnail,
			price: product.salePrice,
			type: product.type,
			gameName: product.gameKorName,
			stock: product.stock,
			requireDonationChannel: product.requireDonationChannel,
			quantity
		});

		setChannel(channel);
		router.push('/checkout');
	};

	const handlePurchase = () => {
		if (!loginCheck()) {
			return;
		}

		if (!product) {
			return;
		}

		if (quantity < 1) {
			setSubmitError('수량은 1 이상이어야 합니다.');
			return;
		}

		setSubmitError(null);

		// 후원 채널 확인 절차 강제 — 채널 미선택 시 (필수 상품이거나 선택안함 미체크면) 모달을 먼저 연다.
		if (!selectedChannel && (product.requireDonationChannel || !optedOut)) {
			setPendingPurchase(true);
			setChannelModalOpen(true);
			return;
		}

		proceedPurchase(selectedChannel);
	};

	const handleAddToCart = () => {
		if (!product) {
			return;
		}

		addItem({
			productID: product.id,
			name: product.name,
			thumbnail: product.thumbnail,
			price: product.salePrice,
			type: product.type,
			gameName: product.gameKorName,
			stock: product.stock,
			requireDonationChannel: product.requireDonationChannel,
			quantity
		});

		setSubmitError(null);
		// 작은 피드백 — 차후 toast 시스템으로 교체 가능
		alert('장바구니에 담았습니다.');
	};

	if (loading) {
		return <Loading />;
	}

	if (error || !product) {
		return (
			<div className='container mx-auto px-4 py-12 text-center'>
				<p className='text-red-600 mb-4'>{error || '상품을 찾을 수 없습니다.'}</p>
				<Link href='/store' className='text-blue-600 underline'>상점으로 돌아가기</Link>
			</div>
		);
	}

	const unitPrice = product.salePrice; // 할인 반영된 단가
	const total = unitPrice * quantity;
	const stockLabel = product.stock === -1 ? '' : `재고 ${product.stock}개`;
	const hasDiscount = product.discountType !== 0 && product.salePrice !== product.price;

	return (
		<div id='store-detail' className='container mx-auto max-w-5xl px-4 py-6'>
			{/* breadcrumb — 모두 파랑 계열 */}
			<nav className='text-sm mb-4 flex flex-wrap items-center gap-1 text-blue-600 dark:text-blue-400'>
				<Link href='/store' className='hover:underline'>상점</Link>
				<span className='text-neutral-400 dark:text-neutral-600 text-xl leading-none mx-0.5'>›</span>
				<Link href={`/store?gameID=${product.gameID}`} className='hover:underline'>
					{product.gameKorName}
				</Link>
				<span className='text-neutral-400 dark:text-neutral-600 text-xl leading-none mx-0.5'>›</span>
				<span className='truncate'>{product.name}</span>
			</nav>

			<div className='grid grid-cols-1 md:grid-cols-[360px_1fr] gap-6 lg:gap-10'>
				{/* 좌측 — 상품 이미지 (작게) */}
				<div>
					<div className='aspect-square bg-neutral-100 dark:bg-neutral-800 rounded-lg overflow-hidden flex items-center justify-center border border-neutral-200 dark:border-neutral-800 sticky md:top-4'>
						{product.thumbnail ? (
							// eslint-disable-next-line @next/next/no-img-element
							<img src={product.thumbnail} alt={product.name} className='w-full h-full max-w-[calc(100%/1.6)] max-h-[calc(100%/1.6)]' />
						) : (
							<span className='text-neutral-400'>이미지 없음</span>
						)}
					</div>
				</div>

				{/* 우측 — 주문 영역 */}
				<div className='min-w-0'>
					<div className='flex items-center gap-2 mb-2'>
						<TypeBadge type={product.type} size='md' />
						<Gamepad2 className='size-4 shrink-0 text-purple-600 dark:text-purple-400' />
						<span className='text-sm font-semibold text-purple-600 dark:text-purple-400'>
							{product.gameKorName}
						</span>
						{product.gameEngName && (
							<span className='text-xs text-neutral-400'>({product.gameEngName})</span>
						)}
					</div>

					<h1 className='text-2xl md:text-3xl font-bold mb-2 break-keep'>{product.name}</h1>

					<div className='text-xs text-neutral-500 mb-4'>
						{product.gameLink ? (
							<a
								href={product.gameLink}
								target='_blank'
								rel='noopener noreferrer'
								className='inline-flex items-center gap-1 hover:underline hover:text-neutral-700 dark:hover:text-neutral-300'
							>
								{product.gamePublisher}
								<ExternalLink className='size-3' aria-hidden />
							</a>
						) : (
							product.gamePublisher
						)}
					</div>

					<div className='border-t border-b border-neutral-200 dark:border-neutral-800 py-4 mb-4'>
						<div className='text-3xl md:text-4xl font-extrabold text-red-600 dark:text-red-400'>
							{formatPrice(unitPrice)}P
						</div>
						{hasDiscount && (
							<div className='flex items-baseline gap-2 mt-1'>
								<span className='text-sm line-through text-yellow-600 dark:text-yellow-400'>
									{formatPrice(product.price)}P
								</span>
								<span className='text-sm font-semibold text-green-600 dark:text-green-400'>
									{product.discountType === 1 ? `-${product.discountValue}%` : `-${formatPrice(product.discountValue)}P`}
								</span>
							</div>
						)}
						{product.stock !== -1 && (
							<div className='text-xs text-neutral-500 mt-1'>{stockLabel}</div>
						)}
					</div>

					<div className='space-y-4 mb-4'>
						<div className='flex items-center gap-4'>
							<label className='text-sm font-semibold w-20 shrink-0'>수량</label>
							<div className='flex items-stretch border border-neutral-300 dark:border-neutral-700 rounded overflow-hidden'>
								<button
									type='button'
									onClick={() => { setQuantity(q => clampQuantity(q - 1, product.stock, product.minPurchase, product.maxPurchase)); }}
									className='px-3 hover:bg-neutral-100 dark:hover:bg-neutral-800'
									aria-label='수량 감소'
								>
									-
								</button>
								<input
									title={`${quantity}개`}
									type='number'
									min={product.minPurchase}
									max={Math.min(product.maxPurchase, product.stock > 0 ? product.stock : HARD_MAX)}
									value={quantity}
									onChange={(e) => {
										const v = parseInt(e.target.value || '1', 10);
										setQuantity(clampQuantity(isNaN(v) ? product.minPurchase : v, product.stock, product.minPurchase, product.maxPurchase));
									}}
									onBlur={(e) => {
										const v = parseInt(e.target.value || '1', 10);
										setQuantity(clampQuantity(isNaN(v) ? product.minPurchase : v, product.stock, product.minPurchase, product.maxPurchase));
									}}
									className='w-14 text-center bg-white dark:bg-neutral-900 outline-none border-0 appearance-none [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none'
								/>
								<button
									type='button'
									onClick={() => { setQuantity(q => clampQuantity(q + 1, product.stock, product.minPurchase, product.maxPurchase)); }}
									className='px-3 hover:bg-neutral-100 dark:hover:bg-neutral-800'
									aria-label='수량 증가'
								>
									+
								</button>
							</div>
							<span className='text-xs text-neutral-500'>{product.minPurchase > 1 ? `최소 ${product.minPurchase}개 / ` : ''}최대 {product.maxPurchase}개</span>
						</div>

						<div className='flex items-center gap-4'>
							<label className='text-sm font-semibold w-20 shrink-0'>후원 채널</label>
							<button
								type='button'
								onClick={() => { if (loginCheck()) { setChannelModalOpen(true); } }}
								className='border border-neutral-300 dark:border-neutral-700 rounded px-3 py-2 text-sm flex-1 text-left bg-white dark:bg-neutral-900 hover:bg-neutral-50 dark:hover:bg-neutral-800 truncate'
							>
								{selectedChannel ? (
									<span className='font-semibold'>{selectedChannel.name}</span>
								) : (
									<span className='text-neutral-400'>후원 채널 선택 (선택)</span>
								)}
							</button>
							{selectedChannel && (
								<button
									type='button'
									onClick={() => { setSelectedChannel(null); }}
									className='text-xs text-neutral-500 underline'
								>
									제거
								</button>
							)}
						</div>
					</div>

					<div className='bg-neutral-100 dark:bg-neutral-800 rounded p-4 mb-4'>
						<div className='flex justify-between text-sm mb-2'>
							<span>상품 금액</span>
							<span>{formatPrice(unitPrice)}P × {quantity}</span>
						</div>
						<div className='flex justify-between font-bold text-lg pt-2 border-t border-neutral-300 dark:border-neutral-700'>
							<span>결제 금액</span>
							<span className='text-red-600 dark:text-red-400'>{formatPrice(total)}P</span>
						</div>
					</div>

					{submitError && (
						<div className='text-red-600 text-sm mb-2'>{submitError}</div>
					)}

					<div className='grid grid-cols-1 sm:grid-cols-[1fr_180px] gap-2 mb-3'>
						<PayButton
							state={payState}
							label={product.stock === 0 ? '품절' : '구매하기'}
							icon={product.stock === 0 ? null : <CreditCard className='size-5' aria-hidden />}
							disabled={product.stock === 0}
							onClick={handlePurchase}
						/>
						<button
							type='button'
							disabled={product.stock === 0}
							onClick={handleAddToCart}
							className='h-[52px] rounded-md border-2 border-yellow-500 text-yellow-700 dark:border-purple-500 dark:text-purple-300 font-bold hover:bg-yellow-50 dark:hover:bg-purple-950/30 disabled:opacity-50 inline-flex items-center justify-center gap-2'
						>
							<ShoppingCart className='size-5' aria-hidden />
							장바구니
						</button>
					</div>

					<p className='text-xs text-neutral-500'>
						* 결제 시 캐시·토큰 잔액에서 차감됩니다.
					</p>
				</div>
			</div>

			{/* 하단 탭 — 상품설명 / 게임소개 */}
			<div className='mt-12'>
				<div className='border-b border-neutral-200 dark:border-neutral-800 flex gap-1'>
					<button
						type='button'
						onClick={() => { setTab('product'); }}
						className={`px-4 py-2 text-sm font-semibold border-b-2 -mb-px ${
							tab === 'product'
								? 'border-blue-600 text-blue-600 dark:text-blue-400 dark:border-blue-400'
								: 'border-transparent text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-300'
						}`}
					>
						상품 설명
					</button>
					<button
						type='button'
						onClick={() => { setTab('game'); }}
						className={`px-4 py-2 text-sm font-semibold border-b-2 -mb-px ${
							tab === 'game'
								? 'border-blue-600 text-blue-600 dark:text-blue-400 dark:border-blue-400'
								: 'border-transparent text-neutral-500 hover:text-neutral-700 dark:hover:text-neutral-300'
						}`}
					>
						게임 소개
					</button>
				</div>

				<div className='py-6'>
					{tab === 'product' && (
						product.description ? (
							<div
								className='product-description ck-content'
								dangerouslySetInnerHTML={{ __html: product.description }}
							/>
						) : (
							<p className='text-neutral-500 text-sm'>상품 설명이 없습니다.</p>
						)
					)}

					{tab === 'game' && (
						<div>
							{product.gameBigImage && (
								<div className='mb-4 rounded-lg overflow-hidden'>
									{/* eslint-disable-next-line @next/next/no-img-element */}
									<img src={product.gameBigImage} alt={product.gameKorName} className='w-full h-auto' />
								</div>
							)}
							<h3 className='text-xl font-bold mb-2'>
								{product.gameKorName}
								{product.gameEngName && (
									<span className='text-neutral-400 text-base ml-2'>({product.gameEngName})</span>
								)}
							</h3>
							<div className='text-sm text-neutral-500 mb-4'>{product.gamePublisher}</div>

							{product.gameDescription ? (
								<div
									className='game-description ck-content'
									dangerouslySetInnerHTML={{ __html: product.gameDescription }}
								/>
							) : (
								<p className='text-neutral-500 text-sm'>게임 소개가 없습니다.</p>
							)}

							{product.gameLink && (
								<a
									href={product.gameLink}
									target='_blank'
									rel='noopener noreferrer'
									className='inline-block mt-4 text-blue-600 dark:text-blue-400 hover:underline text-sm'
								>
									공식 사이트 바로가기 →
								</a>
							)}
						</div>
					)}
				</div>
			</div>

			{/* 같은 게임 다른 상품 */}
			{product.relatedProducts.length > 0 && (
				<div className='mt-12 border-t border-neutral-200 dark:border-neutral-800 pt-8'>
					<h2 className='text-xl font-bold mb-4'>같은 게임의 다른 상품</h2>
					<div className='grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4'>
						{product.relatedProducts.map((r) => (
							<Link
								key={r.id}
								href={`/store/${r.id}`}
								className='group block rounded-lg border border-neutral-200 dark:border-neutral-800 overflow-hidden hover:shadow-lg transition-shadow bg-white dark:bg-neutral-900'
							>
								<div className='aspect-square bg-neutral-100 dark:bg-neutral-800 overflow-hidden flex items-center justify-center'>
									{r.thumbnail ? (
										// eslint-disable-next-line @next/next/no-img-element
										<img src={r.thumbnail} alt={r.name} className='w-full h-full object-scale-down max-w-[calc(100%/1.6)] max-h-[calc(100%/1.6)] group-hover:scale-105 transition-transform duration-300' />
									) : (
										<span className='text-neutral-400 text-xs'>이미지 없음</span>
									)}
								</div>
								<div className='p-3'>
									<div className='flex items-center gap-1.5 mb-1.5'>
										<TypeBadge type={r.type} />
									</div>
									<div className='text-sm font-semibold mb-1.5 line-clamp-2 min-h-[2.5rem]'>{r.name}</div>
									<div className='text-lg font-extrabold text-red-600 dark:text-red-400'>{r.discountType !== 0 && r.salePrice !== r.price ? formatPrice(r.salePrice) : formatPrice(r.price)}P{r.discountType !== 0 && r.salePrice !== r.price ? <span className='ml-1.5 text-xs line-through text-yellow-600 dark:text-yellow-400 font-normal'>{formatPrice(r.price)}P</span> : null}</div>
								</div>
							</Link>
						))}
					</div>
				</div>
			)}

			{channelModalOpen && (
				<ChannelSelectModal
					requireChannel={product.requireDonationChannel}
					onClose={() => { setChannelModalOpen(false); setPendingPurchase(false); }}
					onSelect={(ch) => {
						setSelectedChannel(ch);
						setOptedOut(false);
						addRecent(ch);
						setChannelModalOpen(false);
						if (pendingPurchase) {
							setPendingPurchase(false);
							proceedPurchase(ch);
						}
					}}
					onSkip={() => {
						setSelectedChannel(null);
						setOptedOut(true);
						setChannelModalOpen(false);
						if (pendingPurchase) {
							setPendingPurchase(false);
							proceedPurchase(null);
						}
					}}
				/>
			)}
		</div>
	);
}

function ChannelSelectModal({ onClose, onSelect, onSkip, requireChannel }: {
	onClose: () => void;
	onSelect: (ch: ChannelSearchRow) => void;
	onSkip: () => void;
	requireChannel: boolean;
}) {
	const [keyword, setKeyword] = useState('');
	const [recent, setRecent] = useState<ChannelSearchRow[]>([]);
	const [randoms, setRandoms] = useState<ChannelSearchRow[]>([]);
	const [searchResults, setSearchResults] = useState<ChannelSearchRow[]>([]);
	const [loading, setLoading] = useState(true);
	const [initialized, setInitialized] = useState(false);

	// 모달 mount: localStorage 의 최근 채널을 서버에 검증(stale 자동 제거) 후 표시
	useEffect(() => {
		let cancelled = false;

		(async () => {
			const cached = loadRecent();
			let validated: ChannelSearchRow[] = [];

			if (cached.length > 0) {
				const ids = cached.map(c => c.id).join(',');
				const res = await fetchApi<{ list: ChannelSearchRow[] }>(`/api/store/channels/by-ids?ids=${ids}`, { silent: true });
				if (cancelled) {
					return;
				}

				if (res.success && res.data) {
					// 서버 응답에 포함된 ID 만 유지하고 캐시 순서(최신순) 보존. 서버 최신 정보로 덮어쓰기.
					const fresh = new Map(res.data.list.map(c => [c.id, c]));
					validated = cached.flatMap(c => {
						const f = fresh.get(c.id);
						return f ? [f] : [];
					});
				}

				// 검증 결과로 localStorage 동기화 (stale 제거 + 최신 정보 반영)
				saveRecent(validated);
			}

			if (!cancelled) {
				setRecent(validated);
				setInitialized(true);
			}
		})();

		return () => { cancelled = true; };
	}, []);

	// 검색어 변화에 따른 fetch (debounce 300ms). 검색어 없을 때는 random 으로 부족분 보충.
	useEffect(() => {
		if (!initialized) {
			return;
		}

		const handle = setTimeout(async () => {
			setLoading(true);

			// 검색어 있을 때: 검색 API 만 호출. recent 무시.
			if (keyword.trim()) {
				const params = new URLSearchParams();
				params.set('keyword', keyword.trim());
				params.set('page', '1');
				params.set('perPage', '5');

				const res = await fetchApi<ChannelSearchResponse>(`/api/store/channels/search?${params.toString()}`, { silent: true });

				if (res.success && res.data) {
					setSearchResults(res.data.list);
				}
				else {
					setSearchResults([]);
				}

				setLoading(false);
				return;
			}

			// 검색어 없을 때: recent 5개면 random 호출 안 함. 부족 시 (RECENT_MAX - recent.length) 만큼 random 으로 보충.
			const need = RECENT_MAX - recent.length;
			if (need <= 0) {
				setRandoms([]);
				setLoading(false);
				return;
			}

			const params = new URLSearchParams();
			params.set('random', 'true');
			params.set('page', '1');
			// recent 와 중복 가능성 대비해 여유 있게 가져와서 중복 제거
			params.set('perPage', String(need + recent.length));

			const res = await fetchApi<ChannelSearchResponse>(`/api/store/channels/search?${params.toString()}`, { silent: true });

			if (res.success && res.data) {
				const recentIDs = new Set(recent.map(r => r.id));
				const filtered = res.data.list.filter(c => !recentIDs.has(c.id)).slice(0, need);
				setRandoms(filtered);
			}
			else {
				setRandoms([]);
			}

			setLoading(false);
		}, 300);

		return () => { clearTimeout(handle); };
	}, [keyword, initialized, recent]);

	const handleRemoveRecent = (id: number) => {
		removeRecent(id);
		setRecent(prev => prev.filter(c => c.id !== id));
	};

	// 표시할 리스트: 검색어 있으면 검색 결과만, 없으면 recent + randoms.
	const displayList: { row: ChannelSearchRow; isRecent: boolean }[] = keyword.trim()
		? searchResults.map(row => ({ row, isRecent: false }))
		: [
			...recent.map(row => ({ row, isRecent: true })),
			...randoms.map(row => ({ row, isRecent: false }))
		];

	return (
		<div
			className='fixed inset-0 bg-black/50 z-50 flex items-center justify-center p-4'
			onClick={onClose}
		>
			<div
				className='bg-white dark:bg-neutral-900 rounded-lg w-full max-w-md max-h-[80vh] flex flex-col'
				onClick={(e) => { e.stopPropagation(); }}
			>
				<div className='p-4 border-b border-neutral-200 dark:border-neutral-800'>
					<div className='flex justify-between items-center mb-3'>
						<h2 className='font-bold'>후원 채널 선택</h2>
						<button type='button' onClick={onClose} className='text-neutral-500 hover:text-neutral-700'>✕</button>
					</div>
					<input
						type='search'
						value={keyword}
						onChange={(e) => { setKeyword(e.target.value); }}
						placeholder='채널명, 핸들, 후원 코드 검색'
						className='w-full border border-neutral-300 dark:border-neutral-700 rounded px-3 py-2 text-sm bg-white dark:bg-neutral-900'
						maxLength={100}
					/>
					{!keyword.trim() ? (
						<p className='text-[11px] text-neutral-400 mt-1'>* 최근 후원 채널 후 무작위 순서로 표시됩니다.</p>
					) : (
						<p className='text-[11px] text-neutral-400 mt-1'>* 후원 코드는 정확히 입력해야 합니다.</p>
					)}
				</div>
				<div className='flex-1 overflow-y-auto p-2'>
					{loading ? (
						<Loading type={2} />
					) : displayList.length === 0 ? (
						<div className='text-center py-8 text-neutral-500 text-sm'>채널이 없습니다.</div>
					) : (
						displayList.map(({ row: ch, isRecent }) => (
							<div
								key={ch.id}
								className='w-full flex items-center gap-3 p-3 hover:bg-neutral-100 dark:hover:bg-neutral-800 rounded'
							>
								<button
									type='button'
									onClick={() => { onSelect(ch); }}
									className='flex-1 min-w-0 text-left flex items-center gap-3'
								>
									{ch.thumbnailUrl ? (
										// eslint-disable-next-line @next/next/no-img-element
										<img src={ch.thumbnailUrl} alt='' className='w-10 h-10 rounded-full object-cover shrink-0' />
									) : (
										<div className='w-10 h-10 rounded-full bg-neutral-300 dark:bg-neutral-700 shrink-0' />
									)}
									<div className='flex-1 min-w-0'>
										<div className='font-semibold truncate flex items-center gap-1.5'>
											<span className='truncate'>{ch.name}</span>
											{ch.isVerified && (
												<span className='shrink-0 text-[10px] bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-300 rounded px-1'>✓</span>
											)}
											{ch.donationCode && (
												<span
													className='shrink-0 font-mono text-[11px] px-2 py-[3px] rounded-md tracking-[0.15em] uppercase
														bg-neutral-100 dark:bg-neutral-800
														text-neutral-700 dark:text-neutral-200
														border border-neutral-200/60 dark:border-neutral-700/60
														shadow-[inset_0_1px_2px_rgba(0,0,0,0.18),inset_0_-1px_0_rgba(255,255,255,0.55)]
														dark:shadow-[inset_0_1px_3px_rgba(0,0,0,0.6),inset_0_-1px_0_rgba(255,255,255,0.04)]'
												>
													{ch.donationCode}
												</span>
											)}
										</div>
										{ch.handle && (
											<div className='text-xs text-neutral-500 truncate'>{ch.handle}</div>
										)}
									</div>
								</button>
								{isRecent && (
									<button
										type='button'
										onClick={(e) => {
											e.stopPropagation();
											handleRemoveRecent(ch.id);
										}}
										className='shrink-0 size-6 inline-flex items-center justify-center rounded-full
											text-neutral-400 hover:text-neutral-700 hover:bg-neutral-200
											dark:hover:text-neutral-200 dark:hover:bg-neutral-700'
										aria-label='최근 목록에서 제거'
										title='최근 목록에서 제거'
									>
										✕
									</button>
								)}
							</div>
						))
					)}
				</div>
				{requireChannel ? (
					<div className='p-3 border-t border-neutral-200 dark:border-neutral-800 text-xs text-amber-600 dark:text-amber-400'>
						후원 채널 선택이 필수인 상품입니다.
					</div>
				) : (
					<div className='p-3 border-t border-neutral-200 dark:border-neutral-800'>
						<label className='flex items-center gap-2 text-sm cursor-pointer text-neutral-600 dark:text-neutral-300'>
							<input type='checkbox' onChange={(e) => { if (e.target.checked) { onSkip(); } }} className='size-4' />
							<span>선택 안함</span>
						</label>
					</div>
				)}
			</div>
		</div>
	);
}
